package com.sojson.project.verifycode.service.impl;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.HashMap;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.FastByteArrayOutputStream;

import com.sojson.base.impl.BaseServiceImpl;
import com.sojson.project.verifycode.service.VerifyCodeService;
import com.sojson.util.ali.AliUtil;
import com.sojson.util.cache.CacheUtil;
import com.sojson.util.encrypt.bothway.alike.base64.Base64Util;
import com.sojson.util.file.FileUtil;
import com.sojson.util.mail.MailTemplate;
import com.sojson.util.mail.MailUtil;
import com.sojson.util.servlet.ServletUtil;
import com.sojson.util.sms.SmsUtil;
import com.sojson.util.verifycode.VerifyCodeUtil;
import com.sojson.util.verifycode.VerifyCodeUtil.Verify;

import cn.hutool.core.io.IoUtil;

/**
 * 验证码Service
 *
 * @author liu
 * @date 2020-11-02
 */
@Service
public class VerifyCodeServiceImpl extends BaseServiceImpl implements VerifyCodeService {

    /**
     * 获取验证码
     *
     * @param verifyCodePrefix 验证码前缀
     * @return
     * @throws IOException
     */
    @Override
    public Verify getVerifyCode(String verifyCodePrefix) throws IOException {
        return getVerifyCode(verifyCodePrefix, VerifyCodeUtil.DEFAULT_TIME);
    }

    /**
     * 获取验证码
     *
     * @param verifyCodePrefix 验证码前缀
     * @param timeout 验证码超时时间
     * @return
     * @throws IOException
     */
    @Override
    public Verify getVerifyCode(String verifyCodePrefix, Integer timeout) throws IOException {
        // 生成验证码
        Verify verifyCode = VerifyCodeUtil.generateVerifyCode();

        // 初始化验证码
        initVerifyCode(verifyCode, verifyCodePrefix, timeout);

        // 返回这个验证码
        return verifyCode;
    }

    /**
     * 获取邮箱验证码
     *
     * @param verifyCodePrefix 验证码前缀,用来区分不同验证码
     * @param target 目标邮箱
     * @return
     * @throws Exception
     */
    @Async
    @Override
    public void getVerifyCodeMail(String verifyCodePrefix, String target) throws Exception {
        getVerifyCodeMail(verifyCodePrefix, target, VerifyCodeUtil.DEFAULT_TIME);
    }

    /**
     * 获取邮箱验证码
     *
     * @param verifyCodePrefix 验证码前缀,用来区分不同验证码
     * @param target 目标邮箱
     * @param timeout 验证码超时时间
     * @return
     * @throws Exception
     */
    @Override
    @Async
    public void getVerifyCodeMail(String verifyCodePrefix, String target, Integer timeout) throws Exception {
        // 生成验证码
        Verify verifyCode = VerifyCodeUtil.generateVerifyCode(VerifyCodeUtil.NUMBER_CODES, VerifyCodeUtil.WORD_NUMBER);
        verifyCode.setUsername(target);

        // 初始化验证码
        initVerifyCode(verifyCode, verifyCodePrefix, timeout);

        // 发送这个验证码
        MailUtil.send(target, MailTemplate.VERIFY_CODE.getTitle(), verifyCode.getCode());
    }

    /**
     * 获取短信验证码
     *
     * @param verifyCodePrefix 验证码前缀,用来区分不同验证码
     * @param tel 手机号
     * @return
     * @throws Exception
     */
    @Async
    @Override
    public void getVerifyCodeSms(String verifyCodePrefix, String tel) throws Exception {
        getVerifyCodeSms(verifyCodePrefix, tel, VerifyCodeUtil.DEFAULT_TIME);
    }

    /**
     * 获取短信验证码
     *
     * @param verifyCodePrefix 验证码前缀,用来区分不同验证码
     * @param tel 手机号
     * @param timeout 验证码超时时间
     * @return
     * @throws Exception
     */
    @Async
    @Override
    public void getVerifyCodeSms(String verifyCodePrefix, String tel, Integer timeout) throws Exception {
        // 生成验证码
        Verify verifyCode = VerifyCodeUtil.generateVerifyCode(VerifyCodeUtil.NUMBER_CODES, VerifyCodeUtil.WORD_NUMBER);
        verifyCode.setUsername(tel);

        // 初始化验证码
        initVerifyCode(verifyCode, verifyCodePrefix, timeout);

        // 给短信模板设置参数
        HashMap<String, String> hashMap = new HashMap<>(5);
        hashMap.put("number", verifyCode.getCode());
        // 发送短信
        SmsUtil.sendSms(AliUtil.getSmsTemplateCodeVerifyCode(), hashMap, tel);
    }

    /**
     * 获取验证码图片
     *
     * @param verifyCodePrefix 验证码前缀,用来区分不同验证码
     * @return
     * @throws Exception
     */
    @Override
    public void getVerifyCodeImage(String verifyCodePrefix) throws Exception {
        getVerifyCodeImage(verifyCodePrefix, VerifyCodeUtil.DEFAULT_TIME);
    }

    /**
     * 获取验证码图片
     *
     * @param verifyCodePrefix 验证码前缀,用来区分不同验证码
     * @param timeout 验证码超时时间
     * @return
     * @throws Exception
     */
    @Override
    public void getVerifyCodeImage(String verifyCodePrefix, Integer timeout) throws Exception {
        // 生成验证码
        Verify verifyCode = VerifyCodeUtil.generateVerifyCode();

        // 初始化验证码
        initVerifyCode(verifyCode, verifyCodePrefix, timeout);

        ByteArrayInputStream verifyCodeInputStream = null;
        try {
            // 生成验证码图片
            verifyCodeInputStream = VerifyCodeUtil.getVerifyCodeInputStream(verifyCode.getCode());
            // 向页面输出图片
            ServletUtil.outFileImg(verifyCodeInputStream);
        } finally {
            FileUtil.closeStream(verifyCodeInputStream);
        }
    }

    /**
     * 获取验证码图片流加密后的字符串
     *
     * @param verifyCodePrefix 验证码前缀,用来区分不同验证码
     * @return
     * @throws Exception
     */
    @Override
    public String getVerifyCodeImageByteStr(String verifyCodePrefix) throws Exception {
        return getVerifyCodeImageByteStr(verifyCodePrefix, VerifyCodeUtil.DEFAULT_TIME);
    }

    /**
     * 获取验证码图片流加密后的字符串
     *
     * @param verifyCodePrefix 验证码前缀,用来区分不同验证码
     * @return
     * @throws Exception
     */
    @Override
    public String getVerifyCodeImageByteStr(String verifyCodePrefix, Integer timeout) throws Exception {
        // 生成验证码
        Verify verifyCode = VerifyCodeUtil.generateVerifyCode();

        // 初始化验证码
        initVerifyCode(verifyCode, verifyCodePrefix, timeout);

        ByteArrayInputStream verifyCodeInputStream = null;
        FastByteArrayOutputStream verifyCodeOutStream = null;
        try {
            // 生成验证码图片
            verifyCodeInputStream = VerifyCodeUtil.getVerifyCodeInputStream(verifyCode.getCode());
            verifyCodeOutStream = new FastByteArrayOutputStream();
            // 将输入流放到输出流
            IoUtil.copy(verifyCodeInputStream, verifyCodeOutStream);

            // 返回信息
            return Base64Util.encryptToStr(verifyCodeOutStream.toByteArray());
        } finally {
            FileUtil.closeStream(verifyCodeInputStream, verifyCodeOutStream);
        }
    }

    /**
     * 获取验证码图片在本地目录的路径
     *
     * @param verifyCodePrefix 验证码前缀,用来区分不同验证码
     * @return
     * @throws Exception
     */
    @Override
    public String getVerifyCodeImageThisLocal(String verifyCodePrefix) throws Exception {
        return getVerifyCodeImageThisLocal(verifyCodePrefix, VerifyCodeUtil.DEFAULT_TIME);
    }

    /**
     * 获取验证码图片在本地目录的路径
     *
     * @param verifyCodePrefix 验证码前缀,用来区分不同验证码
     * @param timeout 验证码超时时间
     * @return
     * @throws Exception
     */
    @Override
    public String getVerifyCodeImageThisLocal(String verifyCodePrefix, Integer timeout) throws Exception {
        // 生成验证码
        Verify verifyCode = VerifyCodeUtil.generateVerifyCode();

        // 初始化验证码
        initVerifyCode(verifyCode, verifyCodePrefix, timeout);

        ByteArrayInputStream verifyCodeInputStream = null;
        try {
            // 生成验证码图片
            verifyCodeInputStream = VerifyCodeUtil.getVerifyCodeInputStream(verifyCode.getCode());
            return Base64Util.encrypt(verifyCodePrefix);
        } finally {
            FileUtil.closeStream(verifyCodeInputStream);
        }
    }

    /**
     * 初始化验证码
     *
     * @return 验证码
     * @throws IOException
     */
    private void initVerifyCode(Verify verifyCode, String verifyCodePrefix, Integer timeout) throws IOException {
        // 设置验证码名称
        String verifyCodeName = VerifyCodeUtil.packagingverifyCodeName(verifyCodePrefix);
        verifyCode.setCodeName(verifyCodeName);

        // 保存到缓存
        CacheUtil.setex(verifyCodeName, verifyCode, timeout);
    }

    /**
     * 对比并清空验证码
     *
     * @param verifyCodePrefix 验证码前缀
     * @param code 用户输入的值
     * @return 成功还是失败
     * @throws IOException
     */
    @Override
    public boolean verifyAndClearCode(String verifyCodePrefix, String code) throws IOException {
        // 对比并清空验证码
        return VerifyCodeUtil.verifyAndClearCode(verifyCodePrefix, code);
    }

}